2019_eno_ambient_1_music_for_airports.py

#

SPDX-FileCopyrightText: 2019 Gabriel Châtel & Charles Preham SPDX-FileCopyrightText: 2024 AlICe laboratory https://alicelab.be

SPDX-License-Identifier: GPL-3.0-or-later

#

Blender 2.80 _ Preham _ OSX

BRIAN ENO - Ambient #1 MFA: # formal interpretation by # procedural modeling # # Authors: [C.PREHAM/G.CHÂTEL] # Date: [24.01.2019] # Blender version: [2.8 & hash] # OS: [MacOS 10.13.6] #

#

external functions importation :

import bpy
import random
import math
from math import radians
from math import cos
from math import sin
#

last run supression :

bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete(use_global=False)
#

shortcuts :

cylinder = bpy.ops.mesh.primitive_cylinder_add
cube = bpy.ops.mesh.primitive_cube_add
#

function definition :

#
def smoothhigh(
    high,
):  # function that smooth the height of the next note (next value randomly take +1 or -1)
    highloop = [high - 1, high + 1]
    high = random.choice(highloop)

    if high <= 1:  # bottom limit
        high = 2
    if high >= 5:  # top limit
        high = 4

    return high
#
def smoothradius(
    radius,
):  # function that smooth the radius of the next note (next value randomly take +.1 or -.1)
    radiusloop = [radius - 0.1, radius + 0.1]
    radius = random.choice(radiusloop)

    if radius <= 0.1:  # inner limit
        radius = 0.2
    if radius >= 0.8:  # outer limit
        radius = 0.7

    return radius
#
def score(
    x, y, high, radius
):  # function that make the score (notes (cylinders), lines of notes, ground (floor) and roof (squares and wireframed squares))
    res = []  # list of all lines of notes
    for _ in range(0, squaresize):
        line = []  # list of all notes in a line
        for _ in range(0, squaresize):
            radius = smoothradius(radius)
            high = smoothhigh(high)
            highchoice = [
                high / 2,
                high / 2,
                (10 - high / 2),
            ]  # choice of the position of the column (2/3 on bottom, 1/3 on top)
            z = random.choice(highchoice)
            bpy.ops.surface.primitive_nurbs_surface_cylinder_add(
                radius=radius, enter_editmode=False, location=(x, y, z)
            )
            bpy.context.object.scale[2] = high / (2 * radius)
            line.append(bpy.context.selected_objects[0])

            if x > 5:  # changing some circles into ellipses based on ?????
                bpy.context.object.scale[0] = 0.7
            if y < 5:
                bpy.context.object.scale[1] = 0.7

            roof(x, y, radius, high, z)  # function that make the roof
            x += 2
        res.append(line)
        x = 1
        y += 2
    return res
#
def zone(
    matrice, xindex, yindex
):  # function that make matrices off all 8 (or 5 or 3) neighbours columns of each single column
    for xindex in range(len(matrice[0])):
        for yindex in range(len(matrice[0])):
            if (
                xindex >= 1
                and xindex <= (squaresize - 2)
                and yindex >= 1
                and yindex <= (squaresize - 2)
            ):
                neighbour(xindex, yindex)
#
def neighbour(
    xindex, yindex
):  # function that make a list of all 8 (or 5 or 3) neighbours columns + the middle one
#

. . . . . . . . . . • o . . . . o o o . o o . . . o o . . . . o • o . 8 neighbours • o . . . 5 neighbours . . . . . 3 neighbours . o o o . o o . . . . . . . . . . . . . . . . . . . . . . .

    neighbourlist = []
    neighbourlist.append(matrice[xindex - 1][yindex + 1])
    neighbourlist.append(matrice[xindex][yindex + 1])
    neighbourlist.append(matrice[xindex + 1][yindex + 1])
    neighbourlist.append(matrice[xindex - 1][yindex])
    neighbourlist.append(matrice[xindex][yindex])
    neighbourlist.append(matrice[xindex + 1][yindex])
    neighbourlist.append(matrice[xindex - 1][yindex - 1])
    neighbourlist.append(matrice[xindex][yindex - 1])
    neighbourlist.append(matrice[xindex + 1][yindex - 1])
    print("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-")
    print(" ")
    print("list of columns for checking parameters :")
    print(neighbourlist)
    events(neighbourlist, xindex, yindex, dcu)
#
def coord(
    p1, p2, i, inc, z, name, x, y
):  # function that make a list of 2 points (one above the other) for buildLines ()
#

(point1, point2, index position, incrementation,…)

    (p1, p2) = ([x, y], [x, y])
    p1[i] += inc
    p1.append(z)  # adding z coord to first point

    p2[i] += inc
    p2.append(z + 10)  # adding z coord to second point

    pointXYZ = [p1, p2]
    print("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-")
    print(" ")
    print("list of two coordinates for buildLines () :")
    print(pointXYZ)

    if x >= 5:  # condition for waves
        buildLines(
            pointXYZ, name, 0
        )  # function that make a wave between the two points

    elif x < 5:  # condition for 90° waves
        global p  # count to know the exact index of each part of wave
        p += 1
        buildLines(
            pointXYZ, name, p
        )  # function that make a wave between the two points (90°)
#
def events(neighbourlist, xindex, yindex, dcu):  # function that make append the events
    zcu = 0
    rcu = 0
    for obj in neighbourlist:

        name = obj.name
        point = [bpy.data.objects[name].location[0], bpy.data.objects[name].location[1]]
        pointlist.append(point)
        highcu = bpy.data.objects[name].location[2]
        zcu += highcu  # cumalated height
        radcu = bpy.data.objects[name].dimensions[1] / 2
        rcu += radcu  # cumalated radius
        denscu = bpy.data.objects[name].dimensions[0]
        dcu = denscu  # cumulated density
        print("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-")
        print(" ")
        print("zcu=") and print(zcu)
        print("rcu=") and print(rcu)
        print("dcu=") and print(dcu)

    ground(xindex, yindex, dcu)

    if rcu < 4:  # condition of the first event
        stairs_wall()

    if zcu >= 40:  # condition of the second event
        [x, y] = random.choice(pointlist)
        if x >= 5:
            coord(p1, p2, 0, 0, z, "Halfcircle", x, y)
            coord(p1, p2, 0, 2, z, "Halfcircle2", x, y)

        [x, y] = random.choice(pointlist)
        if x < 5:
            coord(p1, p2, 0, 0, z, "Halfcircle", x, y)
            coord(p1, p2, 1, 2, z, "Halfcircle2", x, y)
#
def buildLines(points, ID, p):  # function that connect points into single lines
#

credit to Michel & Julien for this part of the code

    all = bpy.ops.object.select_all

    for p1, p2 in zip(points, points[1:]):
#

for each point couple (n, n+1)

#

create the Curve Datablock

        curveData = bpy.data.curves.new("myCurves", type="CURVE")
        curveData.dimensions = "3D"
        curveData.resolution_u = 2
#

map coords to spline

        polyline = curveData.splines.new("POLY")
        polyline.points.add(1)
        for i, coord in enumerate([p1, p2]):
            x, y, z = coord
            polyline.points[i].co = (x, y, z, 1)
#

create object

        curveOB = bpy.data.objects.new("myCurves", curveData)
#

apply custom section

        bpy.ops.object.select_all(action="DESELECT")
        curveData.bevel_object = bpy.data.objects[ID]  # custom object
        curveData.use_fill_caps = True
#

attach to scene and validate context

        scn = bpy.context.scene
        scn.collection.objects.link(curveOB)

        if p < 1:
            rotate_wave("myCurves", p)

        if 1 <= p <= 9:
            rotate_wave("myCurves.00", p)

        if p > 9:
            rotate_wave("myCurves.0", p)
#
def rotate_wave(curve, p):
#

function that rotate the waves

    if p < 1:
        name = curve
    if p != 0:
        name = curve + str(p)
    bpy.data.objects[name].select_set(True)
    bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="MEDIAN")
    bpy.ops.transform.rotate(
        value=1.5708,
        orient_axis="Z",
        orient_type="GLOBAL",
        orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
        orient_matrix_type="GLOBAL",
        constraint_axis=(False, False, True),
        mirror=True,
        use_proportional_edit=False,
        proportional_edit_falloff="SMOOTH",
        proportional_size=1,
        use_proportional_connected=False,
        use_proportional_projected=False,
        release_confirm=True,
    )
#
def stairs_wall():
#

function that randomly generate “stairs walls” from outside to inside

    x = random.randrange(0, squaresize * 2, 2)
    y = random.randrange(1, squaresize * 2, 2)
    z = random.choice([1, 9])

    if x <= 5 and y <= 5:  # 10x10 square is divided by 4 to keep walls inside the model
        for i in range(1, 5):
            walls(x, y, z, i)
            x += 1
            y += 1

    if x > 5 and y <= 5:
        for i in range(1, 5):
            walls(x, y, z, i)
            x -= 1
            y += 1

    elif x <= 5 and y > 5:
        for i in range(1, 5):
            walls(x, y, z, i)
            x += 1
            y -= 1

    elif x > 5 and y > 5:
        for i in range(1, 5):
            walls(x, y, z, i)
            x -= 1
            y -= 1
#
def walls(x, y, z, i):
#

function that generate a single wall for one stair

    i = i % 2  # change i to 0 or 1
    bpy.ops.mesh.primitive_plane_add(size=2, enter_editmode=False, location=(x, y, z))
    bpy.context.object.rotation_euler[i] = -1.5708
    fluct(z, 1 - i)
#
def fluct(z, i):
#

function that variate the height of each wall

    var = random.randint(20, 35) / 20
    bpy.context.object.scale[i] = var

    if z == 1:
        bpy.context.object.location[2] = var
    if z == 9:
        bpy.context.object.location[2] = 10 - var
#
def ground(xindex, yindex, dcu):

    global limitground
    limitground = 0
    limitground += 1
    var2 = random.randint(10, 20) / 10

    if limitground == 1:
        floor(xindex, yindex, dcu, var2)
    if limitground == 3:
        floor(xindex, yindex, dcu, var2)
    if limitground == 7:
        floor(xindex, yindex, dcu, var2)
    if limitground == 9:
        floor(xindex, yindex, dcu, var2)

    return zcu
#
def floor(xindex, yindex, dcu, var2):
#

function that make a part of the ground based on the cumulated radius of neighbourlist

    cube(size=1, enter_editmode=False, location=(1 + xindex * 2, 1 + yindex * 2, 10))
    bpy.context.object.scale[0] = 6
    bpy.context.object.scale[1] = 6
    bpy.context.object.scale[2] = (dcu - 20) * 0.05 * var2
    bpy.context.object.location[2] = -(dcu - 20) * 0.05 * var2 / 2
#
def roof(xindex, yindex, zcu, high, z):

    cube(size=1, enter_editmode=False, location=(xindex, yindex, 10 - zcu / 2))
    bpy.context.object.scale[0] = 2
    bpy.context.object.scale[1] = 2
    bpy.context.object.scale[2] = zcu

    if z < 5:  # columnless roof squares
        bpy.ops.object.modifier_add(type="WIREFRAME")
        bpy.context.object.modifiers["Wireframe"].thickness = 0.1
        bpy.context.object.modifiers["Wireframe"].offset = -1
#
def nurbscurve():

    bpy.ops.curve.primitive_nurbs_curve_add(
        radius=1, enter_editmode=False, location=(0, 0, 0)
    )
    bpy.data.objects["NurbsCurve"].scale[1] = 7.999
    bpy.data.objects["NurbsCurve"].scale[0] = 1.334
    bpy.context.scene.cursor.location = (0.0, 6.67, 0.0)
    bpy.ops.object.origin_set(type="ORIGIN_CURSOR", center="MEDIAN")
    for obj in bpy.context.selected_objects:
        obj.name = "Halfcircle"

    bpy.data.objects["Halfcircle"].location[0] = 5
    bpy.data.objects["Halfcircle"].location[1] = 5

    bpy.ops.curve.primitive_nurbs_circle_add(enter_editmode=False, location=(5, 5, 0))
    bpy.context.object.scale[0] = 0.2
    bpy.context.object.scale[1] = 0.2
    for obj in bpy.context.selected_objects:
        obj.name = "Circle"

    bpy.ops.curve.primitive_nurbs_curve_add(
        radius=1, enter_editmode=False, location=(0, 0, 0)
    )
    bpy.data.objects["NurbsCurve"].scale[1] = -7.999
    bpy.data.objects["NurbsCurve"].scale[0] = 1.334
    bpy.context.scene.cursor.location = (0.0, -6.67, 0.0)
    bpy.ops.object.origin_set(type="ORIGIN_CURSOR", center="MEDIAN")
    for obj in bpy.context.selected_objects:
        obj.name = "Halfcircle2"
    bpy.data.objects["Halfcircle2"].location[0] = 5
    bpy.data.objects["Halfcircle2"].location[1] = 5
#

parameters :

squaresize = 5  # number of squares by line
radius = random.randint(1, 20) / 10
high = random.randint(1, 6)
(x, y, z) = (1, 1, 0)
(xindex, yindex) = (0, 0)
(zcu, rcu, dcu) = (0, 0, 0)
p = -1  # count for rotated waves
#

lists :

curvepoint = []
matrice = []
neighbourlist = []
pointlist = []
pointXYZ = []
(p1, p2) = ([], [])
#

execution :

nurbscurve()
matrice = score(x, y, high, radius)
zone(matrice, xindex, yindex)
#

definition de la fonction de création de camera en projection parallele

def axoCam(projection, canon):
    bpy.ops.object.camera_add()
    maScene = bpy.context.scene.render
    monAxoCam = bpy.context.object
    monAxoCam.data.type = "ORTHO"
    monAxoCam.data.ortho_scale = 10

    if projection == "axonometrique":
        if canon == "isometrie":  # OK
            monAxoCam.name = "axoIsometrie"
            monAxoCam.rotation_euler = (radians(54.74), 0.0, radians(45))
            monAxoCam.location = (20.0, -10.0, 20.0)
            maScene.pixel_aspect_x = 1
        if canon == "dimetrie":  # OK
            monAxoCam.name = "axoDimetrie"
            monAxoCam.rotation_euler = (radians(60), 0.0, radians(23))
            monAxoCam.location = (5.53, -13.04, 8.18)
            maScene.pixel_aspect_x = 1
        if canon == "trimetrie":  # OK
            monAxoCam.name = "axoTrimetrie"
            monAxoCam.rotation_euler = (radians(67), 0.0, radians(34))
            monAxoCam.location = (8.59, -12.734, 6.52)
            maScene.pixel_aspect_x = 1

    elif projection == "oblique":
        if canon == "militaire":  # OK
            monAxoCam.name = "oblMilitaire"
            monAxoCam.rotation_euler = (radians(45), 0.0, radians(30))
            monAxoCam.location = (7.071, -12.247, 10 * 1 / cos(radians(45)))
            maScene.pixel_aspect_x = 1 / cos(radians(45))
        if canon == "militaireDiminuee":  # OK
            monAxoCam.name = "oblMilitaireDiminuee"
            monAxoCam.rotation_euler = (radians(45 * 0.82), 0.0, radians(30))
            monAxoCam.location = (5.309, -9.195, 10 * 1 / cos(radians(45)))
            maScene.pixel_aspect_x = 1 / cos(radians(45 * 0.82))
        if canon == "cavalierePlan":  # OK
            monAxoCam.name = "oblCavalierePlan"
            monAxoCam.rotation_euler = (radians(45), 0.0, radians(45))
            monAxoCam.location = (10.0, -10.0, 10 * 1 / cos(radians(45)))
            maScene.pixel_aspect_x = 1 / cos(radians(45))
        if canon == "cavalierePlanDiminuee":  # OK
            monAxoCam.name = "oblCavalierePlanDiminuee"
            monAxoCam.rotation_euler = (radians(45 * 0.82), 0.0, radians(45))
            monAxoCam.location = (7.5, -7.5, 10 * 1 / cos(radians(45)))
            maScene.pixel_aspect_x = 1 / cos(radians(45 * 0.82))
        if canon == "cavaliereAngleCam":  # OK mais à recentrer manuellement
            angleZ = radians(30)  # entrer l'angle de rotation de la camera en Z
            angleZ_XY = radians(
                15
            )  # entrer l'angle d'inclinaison de la camera par rapport au sol
            monAxoCam.name = "oblCavalierePlanDiminuee"
            monAxoCam.rotation_euler = (angleZ_XY, 0.0, angleZ)
            monAxoCam.location = (10, -10, 10 * 1 / cos(radians(45)))
            maScene.pixel_aspect_x = 1 / cos(angleZ_XY)
#

Execution de la fonction de création de la camera choix du type de projection: Effacer le # devant la ligne pour choisir le type de camera à créer

#

PROJECTION OBLIQUE#

axoCam (‘oblique’,’militaire’) axoCam (‘oblique’,’militaireDiminuee’) axoCam (‘oblique’,’cavalierePlan’) axoCam (‘oblique’,’cavalierePlanDiminuee’) axoCam (‘oblique’,’cavaliereAngleCam’) #celle ci peut être réglée en angle de vue -> regarder dans la dernière formule

#

PROJECTION AXONOMETRIQUE#

axoCam("axonometrique", "isometrie")
#

axoCam (‘axonometrique’,’dimetrie’) axoCam (‘axonometrique’,’trimetrie’)